#include "flash_melcep.h"
#include "app_util.h"
#include "nrf_soc.h"

#define FLASH_STATUS_MAX            2

static pstorage_handle_t flash_melcep_handle;
static struct flash_melcep_parm_t flash_write_parm;
static struct flash_melcep_parm_t flash_read_parm;
volatile bool flash_melcep_writing = false;
static volatile bool flash_reading = false;
static volatile bool is_write_fail = false;
static volatile uint16_t flash_write_err_count = 0;
static volatile uint16_t flash_read_err_count = 0;
static uint16_t m_clear_length = 0;

static flash_melcep_status_cb flash_melcep_status_handle[FLASH_STATUS_MAX] = {NULL};
static uint16_t flash_melcep_status_handle_count = 0;

static uint32_t m_flash_melcep_baseID;

static uint32_t m_flash_melcep_cnt = 0;
static uint32_t m_flash_melcep_store_cnt = 0;
static uint8_t m_cur_status = 0;

static flash_melcep_eraser_finish_fun_t flash_melcep_eraser_finish_cb = NULL;

static void flash_melcep_cb_handle(pstorage_handle_t *  p_handle,
                                  uint8_t              op_code,
                                  uint32_t             result,
                                  uint8_t *            p_data,
                                  uint32_t             data_len)
{
    switch(op_code)
    {
        case PSTORAGE_UPDATE_OP_CODE  :
            if(result != NRF_SUCCESS)
            {
                is_write_fail = true;
            }
            m_flash_melcep_cnt++;
            flash_melcep_writing = false;
            NRF_GPIO->OUTCLR = (1UL << 11);
            break;
        case PSTORAGE_LOAD_OP_CODE :
            flash_reading = false ;
            break;
        case PSTORAGE_STORE_OP_CODE:
            m_flash_melcep_store_cnt++;
            NRF_GPIO->OUTCLR = (1UL << 11);
            if(m_flash_melcep_store_cnt >= 10)
            {
                m_flash_melcep_store_cnt = m_flash_melcep_store_cnt;
            }
            break;
        case PSTORAGE_CLEAR_OP_CODE:
            flash_melcep_eraser_finish_cb(0);
            break;
    }
}
#if USE_FLASH_SCHED
static void flash_write_sched_event_handler(void * p_event_data, uint16_t event_size)
{
    uint32_t err;
    struct flash_parm_t * w_parm = (struct flash_parm_t *)p_event_data;
    pstorage_handle_t block_hanle;

    err = pstorage_block_identifier_get(&flash_melcep_handle,w_parm->num,&block_hanle);
    APP_ERROR_CHECK(err);
    err = pstorage_update(&block_hanle,w_parm->buf,w_parm->length,w_parm->offset);
    APP_ERROR_CHECK(err);
    
}


static void flash_read_sched_event_handler(void * p_event_data, uint16_t event_size)
{
    uint32_t err;
    pstorage_handle_t block_hanle;
    struct flash_parm_t * r_parm = (struct flash_parm_t *)p_event_data;

    err = pstorage_block_identifier_get(&flash_melcep_handle,r_parm->num,&block_hanle);
    APP_ERROR_CHECK(err);
    err = pstorage_load(r_parm->buf,&block_hanle,r_parm->length,r_parm->offset);
    APP_ERROR_CHECK(err);
}
#endif

uint32_t flash_melcep_init(bool is_init_pstorage)
{
    uint32_t err;
    pstorage_module_param_t param;
    if(is_init_pstorage)
    {
        err = pstorage_init();
        APP_ERROR_CHECK(err);
    }
    param.block_count = FLASH_MELCEP_BLOCK_NUM;
    param.block_size = FLASH_MELCEP_BLOCK_SIZE;
    param.cb = flash_melcep_cb_handle;
    err= pstorage_register(&param,&flash_melcep_handle);
    APP_ERROR_CHECK(err);
    m_flash_melcep_baseID = (uint32_t)flash_melcep_handle.block_id;
    return err;
}

uint32_t flash_melcep_write(uint32_t block_num,uint8_t *buf,uint32_t length,uint32_t offset)
{
    uint32_t err;
    if(flash_melcep_writing == true)
    {
        flash_write_err_count ++;
        return NRF_ERROR_BUSY;
    }
    flash_melcep_writing = true;
    is_write_fail = false;
    for(int i = 0; i < flash_melcep_status_handle_count; i ++)
    {
        if(flash_melcep_status_handle[i] != NULL)
        {
            flash_melcep_status_handle[i](FLASH_MELCEP_STATUS_WRITE_START);
        }
    }
  
    flash_write_parm.buf = buf;
    flash_write_parm.length = length;
    flash_write_parm.num = block_num;
    flash_write_parm.offset = offset;
    
    #if USE_FLASH_SCHED
    err = app_sched_event_put(&flash_write_parm,sizeof(flash_write_parm),flash_write_sched_event_handler);
    APP_ERROR_CHECK(err);
    #else
    
    struct flash_melcep_parm_t * w_parm = &flash_write_parm;
    pstorage_handle_t block_hanle;
    err = pstorage_block_identifier_get(&flash_melcep_handle,w_parm->num,&block_hanle);
    APP_ERROR_CHECK(err);
    
    if(is_word_aligned((void *)(w_parm->length + offset)) == false)
    {
        APP_ERROR_CHECK(NRF_ERROR_INVALID_PARAM);
    }
    
    err = pstorage_update(&block_hanle,w_parm->buf,w_parm->length,w_parm->offset);
    APP_ERROR_CHECK(err);
    
    #endif

    while(flash_melcep_writing == true)
    {
        #if USE_FLASH_SCHED
        app_sched_execute();
        #endif
    }
    for(int i = 0; i < flash_melcep_status_handle_count; i ++)
    {
        if(flash_melcep_status_handle[i] != NULL)
        {
            flash_melcep_status_handle[i](FLASH_MELCEP_STATUS_WRITE_END);
        }
    }
    if(is_write_fail == true)
    {
        return NRF_ERROR_BUSY;
    }

    return err;
}

uint32_t flash_melcep_read(uint32_t block_num,uint8_t *buf,uint32_t length,uint32_t offset)
{
    
    uint32_t err;
    if(flash_reading == true)
    {
        flash_read_err_count = 0;
        return NRF_ERROR_BUSY;
    }
    flash_reading = true;
    
    flash_read_parm.buf = buf;
    flash_read_parm.length = length;
    flash_read_parm.num = block_num;
    flash_read_parm.offset = offset;
#if USE_FLASH_SCHED
    err = app_sched_event_put(&flash_read_parm,sizeof(flash_read_parm),flash_read_sched_event_handler);
    APP_ERROR_CHECK(err);
#else
    pstorage_handle_t block_hanle;
    struct flash_melcep_parm_t * r_parm = &flash_read_parm;
    err = pstorage_block_identifier_get(&flash_melcep_handle,r_parm->num,&block_hanle);
    APP_ERROR_CHECK(err);
    err = pstorage_load(r_parm->buf,&block_hanle,r_parm->length,r_parm->offset);
    APP_ERROR_CHECK(err);
#endif

    while(flash_reading == true)
    {
#if USE_FLASH_SCHED
        app_sched_execute();
#endif
    }

    return err;
}

void flash_melcep_get_err_info(uint16_t *read_err,uint16_t *write_err)
{
    if(read_err != NULL)
    {
        *read_err = flash_read_err_count;
    }
    
    if(write_err != NULL)
    {
        *write_err = flash_write_err_count;
    }
}

uint32_t flash_melcep_status_evt_reg(flash_melcep_status_cb func)
{
    if(flash_melcep_status_handle_count > FLASH_STATUS_MAX)
    {
        return NRF_ERROR_NO_MEM;
    }
    flash_melcep_status_handle[flash_melcep_status_handle_count ++] = func; 
    return NRF_SUCCESS;
}

uint32_t flash_melcep_clear(uint32_t block_num,uint32_t block_cnt)
{
    uint32_t err_code;
    uint8_t num = 0;

    m_clear_length    = block_cnt * FLASH_MELCEP_BLOCK_SIZE;
    num             = block_num;

    pstorage_handle_t block_hanle;
    err_code = pstorage_block_identifier_get(&flash_melcep_handle,num,&block_hanle);
    APP_ERROR_CHECK(err_code);
    
    if(is_word_aligned((void *)(&m_clear_length)) == false)
    {
        APP_ERROR_CHECK(NRF_ERROR_INVALID_PARAM);
    }

    err_code = pstorage_clear(&block_hanle, m_clear_length);
    APP_ERROR_CHECK(err_code);
}

uint32_t flash_melcep_store(uint32_t block_num,uint8_t *buf,uint32_t length,uint32_t offset)
{
    uint32_t err_code;
    struct flash_melcep_parm_t * w_parm = &flash_write_parm;
    
    flash_write_parm.buf = buf;
    flash_write_parm.length = length;
    flash_write_parm.num = block_num;
    flash_write_parm.offset = offset;
    pstorage_handle_t block_hanle;
    err_code = pstorage_block_identifier_get(&flash_melcep_handle,w_parm->num,&block_hanle);
    APP_ERROR_CHECK(err_code);
    
    if(is_word_aligned((void *)(w_parm->length + offset)) == false)
    {
        APP_ERROR_CHECK(NRF_ERROR_INVALID_PARAM);
    }
    
    err_code = pstorage_store(&block_hanle,w_parm->buf,w_parm->length,w_parm->offset);
    APP_ERROR_CHECK(err_code);
}

uint32_t flash_melcep_get_startADD(void)
{
    return m_flash_melcep_baseID;
}


bool flash_melcep_eraser_finish_reg(flash_melcep_eraser_finish_fun_t fun)
{
    flash_melcep_eraser_finish_cb = fun;
    return true;
}

